package com.jacky.demo.widget.camera;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.util.Range;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import android.widget.Toast;

import com.jacky.demo.R;
import com.jacky.log.Logger;

import java.io.File;
import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * Created by lixinquan on 2020/4/10.
 *
 * 使用 Camera2.0 API
 */
public class CameraFragment extends Fragment implements View.OnTouchListener {

    private static final String CAMERA_FRONT = "1";
    private static final String CAMERA_BACK = "0";

    private OnImageListener onImageListener;
    public void setImageListener(OnImageListener listener) {
        onImageListener = listener;
    }

    private OnDoubleTapListener mDoubleTapListener;
    public void setDoubleClick(OnDoubleTapListener listener) {
        mDoubleTapListener = listener;
    }

    public boolean isCameraFacingBack() {
        return CAMERA_BACK.equals(currentCameraId);
    }

    public int getNumberOfCameras() {
        return Camera.getNumberOfCameras();
    }

    public boolean toggleFlashLight() {
        if(captureSession == null || previewRequestBuilder == null) return false;

        previewRequestBuilder.set(CaptureRequest.FLASH_MODE,
                isFlashOpen ? CaptureRequest.FLASH_MODE_OFF : CaptureRequest.FLASH_MODE_TORCH);
        if(!repeatPreviewRequest()) isFlashOpen = false;
        else isFlashOpen = !isFlashOpen;
        return isFlashOpen;
    }
    private final SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if(seekBar == mSeekBarBright) {
                changeAppBrightness(progress);
            } else {
                if(characteristics == null || captureSession == null) return;
                Range<Integer> range = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
                if(range == null) return;
                int cp = progress + range.getLower();
                if(cp > range.getUpper()) cp = range.getUpper();
                previewRequestBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, cp);
                repeatPreviewRequest();
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {}

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {}
    };
    private final ImageReader.OnImageAvailableListener mOnJpegImageAvailableListener = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
//            Logger.w("reader ", Thread.currentThread().getName());
            Image image = reader.acquireLatestImage();
            if(image == null) return;
            ByteBuffer buffer = image.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            image.close();

            if(onImageListener != null) {
                onImageListener.onImage(bytes, orientationListener.getRotation());
            }
        }
    };
    private final CameraDevice.StateCallback openCameraCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            cameraDevice = camera;
            try {
                initCamera();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            cameraDevice = CameraUtils.close(cameraDevice);
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            onDisconnected(camera);
            Log.e("camera", "error : " + error);
        }
    };
    TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            onSurfaceTextureSizeChanged(surface, width, height);
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
            isRatio43 = CameraUtils.aspectRatio(width, height);
            try {
                initCamera();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
    };
    private final CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
            super.onCaptureStarted(session, request, timestamp, frameNumber);
        }

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
            super.onCaptureProgressed(session, request, partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
        }
    };

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.camera2_fragment, container, false);
    }

    private AutoFitTextureView mView;
    private CameraDevice cameraDevice;
    private CameraCaptureSession captureSession;
    private ImageReader imageReader;
    private CameraCharacteristics characteristics;
    private CaptureRequest.Builder previewRequestBuilder = null;

    private Handler mBackgroundHandler;
    private HandlerThread mBackgroundThread;
    private boolean isRatio43 , isFlashOpen, isScreenLock, mAFRun;
    private String currentCameraId = CAMERA_BACK;
    private MyOrientationEventListener orientationListener;

    private View mLockScreenView, mLockTips;
    private TouchDrawView mFocusView;
    private SeekBar mSeekBarBright, mSeekBarExposure;

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if(v == mLockScreenView) return isScreenLock;
        return onTouchEvent(v, event);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        orientationListener = new MyOrientationEventListener(getActivity());
        mLockTips = view.findViewById(R.id.lock_tips);
        mFocusView = view.findViewById(R.id.touch_view);
        mView = view.findViewById(R.id.texture_view);
        mView.setSurfaceTextureListener(surfaceTextureListener);
        mView.setOnTouchListener(this);
        mSeekBarBright = view.findViewById(R.id.seek_bar_bright);
        mSeekBarBright.setOnSeekBarChangeListener(seekBarChangeListener);
        mSeekBarExposure = view.findViewById(R.id.seek_bar_exposure);
        mSeekBarExposure.setOnSeekBarChangeListener(seekBarChangeListener);

        mView.postDelayed(new Runnable() {
            @Override
            public void run() {
                //设置曝光度
                Range<Integer> range = characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
                if(range != null) {
                    int mi = range.getLower(), ma = range.getUpper();
                    int max = ma - mi;
                    Logger.i(ma, mi, max);
                    mSeekBarExposure.setAlpha(1);
                    mSeekBarExposure.setMax(max);
                    mSeekBarExposure.setProgress(0 - mi);
                }
                //将亮度进度条移到最右边
                mSeekBarBright.setProgress(CameraUtils.getActivityBrightness(getActivity()));
                int x = (mView.getWidth() - mSeekBarBright.getHeight())/2;
                mSeekBarBright.animate().translationX(x).alpha(1).setDuration(1);
                //锁定屏幕的界面
                mLockScreenView = new View(getActivity());
                mLockScreenView.setOnTouchListener(CameraFragment.this);
                getActivity().getWindow().addContentView(mLockScreenView,
                        new FrameLayout.LayoutParams(-1, -1));
            }
        }, 2000);
    }

    @Override
    public void onResume() {
        super.onResume();
        mBackgroundThread = new HandlerThread("CameraThread");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
        orientationListener.enable();
        openCamera(currentCameraId);
    }

    @Override
    public void onPause() {
        super.onPause();
        closeCamera();
        mBackgroundThread.quitSafely();
        try {
            mBackgroundThread.join();
            mBackgroundThread = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mBackgroundHandler = null;
        orientationListener.disable();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        orientationListener = null;
    }

    public boolean isScreenLock() {
        return isScreenLock;
    }

    public void toggleLock() {
        isScreenLock = !isScreenLock;
        if(isScreenLock) {
            mLockTips.setVisibility(View.VISIBLE);
            Toast.makeText(getActivity(), "您开启了锁屏功能", Toast.LENGTH_SHORT).show();
        } else {
            mLockTips.setVisibility(View.GONE);
            Toast.makeText(getActivity(), "您关闭了锁屏功能", Toast.LENGTH_SHORT).show();
        }
    }

    public void noSeekBar() {
        ViewGroup view = (ViewGroup) getView();
        if(view == null) return;
        view.removeView(mSeekBarBright);
        view.removeView(mSeekBarExposure);
    }

    private TouchRunnable touchRunnable = new TouchRunnable();

    private boolean onTouchEvent(View v, MotionEvent event) {
        int count = event.getPointerCount();
        //Logger.e("count ", count, event.getAction());
        if(count == 1) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                touchRunnable.setXY(event.getX(), event.getY());
                mFocusView.postDelayed(touchRunnable, 100);
            }
            return true;
        } else if(count >= 2) { //多个手指调焦
            return onTouchTwoPoint(v, event);
        }
        return false;
    }
    //当时当前的zoom
    private double zoom;
    //上次缩放前的zoom
    private double lastZoom;
    //两个手刚一起碰到手机屏幕的距离
    private double length;
    private int maxRealRadio;
    private Rect maxZoomRect, picRect;
    private boolean onTouchTwoPoint(View v, MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_POINTER_DOWN:
                mFocusView.removeCallbacks(touchRunnable);
                if(maxZoomRect == null) {
                    maxZoomRect = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
                    Float f = characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
                    maxRealRadio = f == null ? 1 : f.intValue();
                    picRect = new Rect();
                }
                length = CameraUtils.distance(event);
                break;
            case MotionEvent.ACTION_POINTER_UP:
                lastZoom = zoom;
                break;
            case MotionEvent.ACTION_MOVE:
                Double lenthRec = CameraUtils.distance(event) - length;
                int w = v.getWidth(), h = v.getHeight();
                Double viewLenth = Math.sqrt(w * w + h * h);
                zoom = ((lenthRec / viewLenth) * maxRealRadio) + lastZoom;
                if(zoom > maxRealRadio) zoom = maxRealRadio;
                else if(zoom <= 1) zoom = 1;

                int cx = maxZoomRect.centerX(), cy = maxZoomRect.centerY();
                double cw = maxZoomRect.width() / zoom /2, ch = maxZoomRect.height() / zoom/2;
                picRect.top = (int) (cy - ch);
                picRect.left = (int) (cx - cw);
                picRect.right = (int) (cx + cw);
                picRect.bottom = (int) (cy + ch);
                Logger.w("zoom: ", zoom, maxRealRadio, picRect);

                previewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, picRect);
                repeatPreviewRequest();
                break;
        }
        return true;
    }

    public void takePicture() {
        if (captureSession == null || cameraDevice == null) return;
        try {
            CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(imageReader.getSurface());
            captureBuilder.set(CaptureRequest.FLASH_MODE,
                    isFlashOpen ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
            captureBuilder.set(CaptureRequest.SCALER_CROP_REGION, picRect);
//            CameraUtils.setup3AControlsLocked(captureBuilder, characteristics);
            //执行拍照动作
            captureSession.capture(captureBuilder.build(), null, mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    public void switchCamera() {
        if (currentCameraId.equals(CAMERA_BACK)) {
            currentCameraId = CAMERA_FRONT;
        } else {
            currentCameraId = CAMERA_BACK;
        }
        closeCamera();
        if (mView.isAvailable()) {
            openCamera(currentCameraId);
        } else {
            mView.setSurfaceTextureListener(surfaceTextureListener);
        }
    }
    @SuppressLint("MissingPermission")
    private void openCamera(String cameraId) {
        Logger.w("open camera id..", cameraId);
        isFlashOpen = false;

        CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
        try {
            characteristics  = manager.getCameraCharacteristics(cameraId);
            manager.openCamera(cameraId, openCameraCallback, mBackgroundHandler);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    private void closeCamera() {
        captureSession = CameraUtils.close(captureSession);
        cameraDevice = CameraUtils.close(cameraDevice);
        imageReader = CameraUtils.close(imageReader);
        maxZoomRect = null;
        picRect = null;
        lastZoom = 0;
    }

    private void initCamera() throws CameraAccessException {
        if(characteristics == null || cameraDevice == null || !mView.isAvailable()) {
            return;
        }
//        int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
//        int totalRotation = CameraUtils.sensorToDeviceRotation(characteristics, deviceRotation);
//        Logger.w("rotation", totalRotation);
//        boolean swappedDimensions = true;//totalRotation == 90 || totalRotation == 270;
        StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        if(map == null) return;

        // For still image captures, we always use the largest available size.
        Size largestJpeg = CameraUtils.findFitSize(isRatio43, map.getOutputSizes(ImageFormat.JPEG));
        Size previewSize = CameraUtils.findFitSize(isRatio43, map.getOutputSizes(SurfaceTexture.class));
        CameraUtils.close(imageReader); //清理旧的
        imageReader = ImageReader.newInstance(largestJpeg.getWidth(),
                largestJpeg.getHeight(), ImageFormat.JPEG, 2);
        imageReader.setOnImageAvailableListener(mOnJpegImageAvailableListener, mBackgroundHandler);

        Logger.i(largestJpeg, previewSize);
//        if (swappedDimensions) {
        mView.setAspectRatio(previewSize.getHeight(), previewSize.getWidth());
//        } else {
//            mView.setAspectRatio(previewSize.getWidth(), previewSize.getHeight());
//        }
        SurfaceTexture texture = mView.getSurfaceTexture();
        texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
        final Surface holder = new Surface(texture);
        cameraDevice.createCaptureSession(Arrays.asList(holder, imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                if(cameraDevice == null) return;
                CameraUtils.close(captureSession);

                captureSession = session;
                try {
                    previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//                    mAFRun = CameraUtils.setup3AControlsLocked(previewRequestBuilder, characteristics);
                    previewRequestBuilder.addTarget(holder);
                    repeatPreviewRequest();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                Logger.e("create session failed...");
            }
        }, mBackgroundHandler);
    }

    private class MyOrientationEventListener extends OrientationEventListener {

        private int orientation;

        MyOrientationEventListener(Context context) {
            super(context);
        }

        @Override
        public void onOrientationChanged(int orientation) {
            if(orientation == ORIENTATION_UNKNOWN) {
                Logger.w("orientations", orientation);
                return;
            }
            this.orientation = orientation;
        }

        private int getRotation() {
            int rotation;
            if(orientation > 325 || orientation <= 45){
                rotation = isCameraFacingBack() ? 90 : 270;
            }else if(orientation <= 135){
                rotation = 180;
            }else if(orientation <= 225){
                rotation = isCameraFacingBack() ? 270 : 90;
            }else {
                rotation = 0;
            }
            return rotation;
        }
    }

    private class TouchRunnable implements Runnable {
        private float x, y;
        public void setXY(float dx, float dy) {
            x = dx;
            y = dy;
        }
        @Override
        public void run() {
            if(mFocusView.isDoubleTap(x, y)) {
                if(mDoubleTapListener != null) mDoubleTapListener.onDoubleTap();
            } else {
                mFocusView.invalidate(x + mView.getLeft(), y + mView.getTop());
                CameraUtils.focusOnTouch(previewRequestBuilder,characteristics, x, y, mAFRun);
                repeatPreviewRequest();
            }
        }
    }
    /**
     * 改变App当前Window亮度
     *
     * @param brightness 屏幕亮度
     */
    private void changeAppBrightness(@IntRange(from = 0, to = 255)int brightness) {
        if(isScreenLock || mLockScreenView == null) return;

        Activity activity = getActivity();
        if(activity == null) return;
        Window window = activity.getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        if (brightness == -1) {
            lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
        } else {
            lp.screenBrightness = (brightness <= 0 ? 1 : brightness) / 255f;
        }
        window.setAttributes(lp);
        mLockScreenView.setBackgroundColor(Color.argb(255 - brightness, 0, 0,0));
    }

    private boolean repeatPreviewRequest() {
        try {
            captureSession.setRepeatingRequest(previewRequestBuilder.build(), captureCallback, mBackgroundHandler);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 将图片数据保存到文件中
     * @param data   图片数据
     * @param rotation 图片拍摄时旋转的角度
     * @param file 图片需要保存的文件名
     */
    public static void saveImage(byte[] data, int rotation, File file) {
        CameraUtils.saveImage(data, rotation, file);
    }
}
